/*
 * edbd_dbg.c - EDB debug realted
 * Clunix, cezanne@clunix.com, 2005.4
 *
 * Copyright (c) 2001-2005 by Clunix Inc. All Rights Reserved.
 */

#include "config.h"

#include "edbd.h"
#include "reactor.h"
#include "bdexpr.h"
#include "edbd_log.h"
#include "edbd_conn.h"
#include "edbd_vsr.h"
#include "edbroot.h"

#ifdef ECM_DEBUG

static ECM_INLINE void
dbg_ERROR(char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	vprintf(fmt, ap);
	va_end(ap);

	printf("\nEDBD Halt. For Debugging\n");
	exit(0);
}

static ECM_INLINE void
dbg_WARNING(char *fmt, ...)
{
	va_list ap;

	printf("WARNING: ");
	va_start(ap, fmt);
	vprintf(fmt, ap);
	va_end(ap);
	printf("\n");
}

static ECM_INLINE ecm_size_t
dbg_snprintf(char *buf, ecm_size_t len, const char *format, ...)
{
	va_list	ap;
	ecm_size_t	cnt;

	va_start(ap, format);
	cnt = ecm_vsnprintf(buf, len, format, ap);
	va_end(ap);

	if (cnt > len)
		cnt = len;
	if (cnt < 0)
		return 0;
	return cnt;
}

char *
dbg_get_ipstr(sr_t *sr_ip)
{
	static char	buf[256];
	char	*ipstr;

	if (sr_ip == NULL)
		return "";
	sr_asrv_asprint(&ipstr, ST_IP, sr_get_val(sr_ip));
	ecm_snprintf(buf, 256, "%s", ipstr);
	ecm_free(ipstr);
	return buf;
}

char *
dbg_get_opt(int opt)
{
	static char	buf[256];
	char		*optstr, *p;

	if (opt & SR_CHILD)
		optstr = ecm_strdup("SR_CHILD");
	else
		optstr = ecm_strdup("");
	if (opt & SR_RECUR) {
		ecm_asprintf(&p, "%s%sSR_RECUR",
			     optstr, ecm_empty_str(optstr) ? "": "|");
		ecm_free(optstr);
		optstr = p;
	}
	if (opt & SR_NO_DEREF) {
		ecm_asprintf(&p, "%s%sSR_NO_DEREF",
			     optstr, ecm_empty_str(optstr) ? "": "|");
		ecm_free(optstr);
		optstr = p;
	}
	strncpy(buf, optstr, 256);
	ecm_free(optstr);
	return buf;
}

char *
dbg_get_field(edb_field_t field)
{
	char	*fieldstr[] = {
		"name", "type", "val", "owner",
		"update", "fetch", "system", "reactor"
	};
	return fieldstr[field];
}

char *
dbg_get_LNP(sr_t *sr_LNP)
{
	static char	buf[256];
	char	*LNP;

	LNP = string_LNP(sr_LNP);
	ecm_snprintf(buf, 256, "%s", ecm_safe_str(LNP));
	ecm_free(LNP);
	return buf;
}

char *
dbg_get_memLNP(sr_t *sr_memLNP)
{
	static char	buf[256];

	strncpy(buf, dbg_get_LNP(sr_memLNP), 256);
	return buf;
}

char *
dbg_get_tree_LNP(const sr_t *sr)
{
	static char	buf[256];
	char	*LNP;

	LNP = vsr_get_LNP(sr);
	ecm_snprintf(buf, sizeof buf, "%s", ecm_safe_str(LNP));
	ecm_free(LNP);
	return buf;
}

static void
escape_edexpr(char **pbuf, ecm_uint32 *pnbuf, const char *edexpr)
{
	char	*p, *end;
	const char	*q;
	ecm_uint32	nbuf;

	if (ecm_empty_str(edexpr))
		return;

	q = edexpr;
	p = *pbuf;
	nbuf = *pnbuf;
	end = p + nbuf - 1;

	while (p < end && *q) {
		switch (*q) {
		case ']':
		case ';':
			*p++ = '\\';
			break;
		default:
			break;
		}
		*p++ = *q++;
	}
	*p = '\0';
	*pbuf = p;
	*pnbuf -= (ecm_uint32)(p - *pbuf);
}

char *
dbg_get_edexprs(const sr_t *sr_edexprs)
{
	static char	buf[1024];
	const sr_t	*sr_edexpr;
	char	*p, *edexpr;
	ecm_uint32	nbuf = 1022;

	p = buf;
	*p++ = '[';
	SR_FOREACH_CHILD(sr_edexprs, NULL, sr_edexpr) {
		edexpr = edl_sr_to_edexpr(sr_edexpr);
		escape_edexpr(&p, &nbuf, edexpr);
		if (sr_next(sr_edexpr)) {
			*p++ = ';';
			nbuf--;
		}
		ecm_free(edexpr);
	}
	*p++ = ']';
	*p = '\0';
	return buf;
}

char *
dbg_get_edexpr(const sr_t *sr_edexpr)
{
	static char	buf[512];
	char	*edexpr, *p;
	ecm_uint32	nbuf = 510;

	p = buf;
	*p++ = '[';
	edexpr = edl_sr_to_edexpr(sr_edexpr);
	escape_edexpr(&p, &nbuf, edexpr);
	ecm_free(edexpr);
	*p++ = ']';
	*p = '\0';
	return buf;
}

static ECM_INLINE char *
get_log_type_str(logtype_t logtype)
{
	static char	*str_logtypes[] = {
		"NONE", "APPEND", "SET", "SETREF", "SETEXT", "DEL",
		"XECREATE", "XEDELETE", "CS", "XEAPPEND"
	};

	if (logtype < 0 || logtype >= EDBD_LOGTYPE_EOF)
		return "<invalid>";
	return str_logtypes[logtype];
}

char *
dbg_get_conn_info(edbd_conn_t edbd_conn)
{
	static char	buf[128];
	conn_t		*conn = (conn_t *)edbd_conn;

	if (conn == NULL)
		return "empty conn";
	ecm_snprintf(buf, sizeof buf, "conn:<%s>",
		     ecm_safe_str(conn->username));
	return buf;
}

char *
dbg_get_reactor_info(reactor_t *reactor)
{
	static char	buf[512];

	if (reactor == NULL)
		return "empty reactor";
	if (reactor->valid)
		ecm_snprintf(buf, sizeof buf, "[%s]@%s", reactor->edexpr, reactor->LNP);
	else
		ecm_snprintf(buf, sizeof buf, "INVALID[%s]", reactor->edexpr);

	return buf;
}

char *
dbg_get_lnpsensor_info(lnpsensor_t *lnpsensor)
{
	static char	buf[512];
	char		*typestr;
	char		*pivot_LNP;
	char		*trail_LNP;
	char		*edexpr;

	if (lnpsensor == NULL)
		return "empty lnpsensor";
	switch (lnpsensor->type) {
	case LNPSENSOR_WCARD:
		typestr = "wcard";
		break;
	case LNPSENSOR_REF:
		typestr = "ref";
		break;
	case LNPSENSOR_PLAIN:
		typestr = "plain";
		break;
	default:
		typestr = "";
	}

	if (lnpsensor->bdexpr->reactor == NULL)
		return "<empty reactor>";
	edexpr = lnpsensor->bdexpr->reactor->edexpr;

	pivot_LNP = sr_get_LNP(lnpsensor->sr_pivot);
	trail_LNP = string_LNP(lnpsensor->sr_trail_LNP);
	ecm_snprintf(buf, sizeof buf, "%s(%s,%s) from [%s]",
		     typestr, pivot_LNP, trail_LNP,
		     edexpr);
	ecm_free(pivot_LNP);
	ecm_free(trail_LNP);
	return buf;
}

char *
dbg_get_log_info(const sr_t *sr_log)
{
	static char	buf[512];
	int		len = 0;
	logtype_t	logtype;

	logtype = sr_geti_int(sr_log, 0);
	len += dbg_snprintf(buf + len, sizeof(buf) - len, "%s", get_log_type_str(logtype));
	switch (logtype) {
	case EDBD_LOGTYPE_APPEND:
		ecm_snprintf(buf + len, sizeof(buf) - len, "LNP: %s", sr_geti_str(sr_log, 1));
		break;
	case EDBD_LOGTYPE_SET: {
		sr_t	*sr;
		const char	*LNP = sr_geti_str(sr_log, 1);

		sr = edbroot_getn(sr_edbroot, LNP);
		if (sr) {
			char	*asrv;

			if (sr_geti(sr_log, 2) == NULL)
				dbg_ERROR("OOPS: Value not exist");

			sr_asrv_asprint(&asrv, vsr_get_type(sr), sr_get_val(sr_geti(sr_log, 2)));
			ecm_snprintf(buf + len, sizeof(buf) - len, " LNP: %s ASRV: %s", LNP, asrv);
			ecm_free(asrv);
			edbroot_release(sr, ECM_TRUE);
		}
		else
			dbg_WARNING("Log info ERROR: LNP(%s) not exist", LNP);

		break;
	}
	case EDBD_LOGTYPE_SETREF: {
		ecm_snprintf(buf + len, sizeof(buf) - len, "LNP: %s REFERER: %s TARGET: %s",
			     sr_geti_str(sr_log, 1),
			     sr_geti_str(sr_log, 2),
			     sr_geti_str(sr_log, 3));
		break;
	}
	case EDBD_LOGTYPE_SETEXT: {
		char	*update, *owner, *edexprs;

		sr_asrv_asprint(&update, ST_ATTR_UPDATE, sr_val_int(sr_geti_int(sr_log, 3)));
		sr_asrv_asprint(&owner, ST_OWNER, sr_val_ll(sr_geti_ll(sr_log, 2)));
		edexprs = edl_sr_to_edexpr(sr_geti(sr_log, 4));

		ecm_snprintf(buf + len, sizeof(buf) - len, " LNP: %s Owner: %s Update: %s Reactor: %s",
			     sr_geti_str(sr_log, 1),
			     owner, update, edexprs);
		
		ecm_free(update);
		ecm_free(owner);
		ecm_free(edexprs);
		break;
	}
	case EDBD_LOGTYPE_DEL:
		ecm_snprintf(buf + len, sizeof(buf) - len, " LNP: %s", sr_geti_str(sr_log, 1));
		break;
	case EDBD_LOGTYPE_XECREATE:
		ecm_snprintf(buf + len, sizeof(buf) - len, " LNP: %s Name: %s", sr_geti_str(sr_log, 1), sr_geti_str(sr_log, 2));
		break;
	case EDBD_LOGTYPE_XEDELETE:
		ecm_snprintf(buf + len, sizeof(buf) - len, " LNP: %s", sr_geti_str(sr_log, 1));
		break;
	case EDBD_LOGTYPE_XEAPPEND:
		ecm_snprintf(buf + len, sizeof(buf) - len,
			     " LNP: %s Name: %s",
			     sr_geti_str(sr_log, 1),
			     sr_get_name(sr_geti(sr_log, 2)));
		break;
	case EDBD_LOGTYPE_CS:
		ecm_snprintf(buf + len, sizeof(buf) - len, " name: %s, type: %s",
			     sr_geti_str(sr_log, 1),
			     sr_geti_str(sr_log, 2));
		break;
	default:
		break;
	}
	return buf;
}

const char *
dbg_get_treelock_str(treelock_t type)
{
	static char	*treelock_strs[] = {
		"PREX",
		"R EX",
		"  EX",
		"R SH",
		"  SH",
		"PIN "
	};
	if (type == PIN_FREE_LOCK)
		return "PINF";
	else if (type == PIN_VALID_LOCK)
		return "PINV";
	return treelock_strs[type];
}

const char *
dbg_get_sublock_str(int type)
{
	static char	*sublock_strs[] = {
		"NONE", "PRX", "RX", "X", "RS", "S", "PIN", "BX", "BS",
		"S_BX", "RS_BX", "RS_X", "ERROR"
	};
	return sublock_strs[type];
}

#endif
